﻿//     Copyright (c) Microsoft Corporation.  All rights reserved.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Gateway.UpdateService.Models;
using Microsoft.WindowsAzure.Storage.Blob;
using Newtonsoft.Json;

namespace Gateway.UpdateService.Manifests
{
    public class StorageManifests
    {
        const string ManifestFileName = "package.manifest";

        private StorageManifests()
        {
        }

        public static async Task<StorageManifests> CreateAsync(string containerUrl, string rootDirPath, bool includeDisabled)
        {
            var container = new CloudBlobContainer(new Uri(containerUrl));
            var rootDir = container.GetDirectoryReference(rootDirPath);
            var deserializer = JsonSerializer.Create(new JsonSerializerSettings
            {
                Error = (sender, args) =>
                {
                    Trace.TraceWarning("Error deserializing package manifest object [{0}]. Details: {1}", "", args.ErrorContext.Error.Message);
                    args.ErrorContext.Handled = true;
                },
            });
            var resultTasks = rootDir.ListBlobs()
                .OfType<CloudBlobDirectory>()
                .Select(async versionDir =>
                {
                    try
                    {
                        var manifestBlob = versionDir.GetBlockBlobReference(ManifestFileName);
                        using (var manifestStream = await manifestBlob.OpenReadAsync())
                        {
                            var manifest = deserializer.Deserialize<PackageManifest>(new JsonTextReader(new StreamReader(manifestStream)));
                            manifest.RootUri = versionDir.Uri.ToString();
                            return manifest;
                        }
                    }
                    catch (Exception ex)
                    {
                        Trace.TraceWarning("Failed to load and deserialize manifest for directory [{0}]. Details: {1}", versionDir.Uri, ex);
                        return null;
                    }
                });
            return new StorageManifests
            {
                Manifests = (await Task.WhenAll(resultTasks))
                    .Where(manifest => manifest != null && manifest.Version != null && (!manifest.Disabled || includeDisabled))
                    .OrderBy(manifest => manifest.Version),
            };
        }

        public IEnumerable<PackageManifest> Manifests { get; private set; }

        public PackageManifest Manifest(Version manifestVersion)
        {
            return this.Manifests
                .FirstOrDefault(manifest => manifest.Version == manifestVersion);
        }

        public PackageManifest ManifestMoreRecent(Version currentVersion)
        {
            // this.Manifests is already ordered, so we just have to get the last one if it is greater than currentVersion
            var mostRecentManifest = this.Manifests.LastOrDefault();
            if (mostRecentManifest != null && mostRecentManifest.Version > currentVersion)
            {
                return mostRecentManifest;
            }
            return null;
        }

        public static async Task<ICloudBlob> GetManifestFileBlobAsync(PackageManifest manifest, string packageName, string fileName)
        {
            var requestPackage = manifest.AvailablePackages
                .FirstOrDefault(package => String.Equals(package.PackageName, packageName, StringComparison.OrdinalIgnoreCase));
            if (requestPackage == null)
            {
                return null;
            }
            string storageFile = requestPackage.StorageFileNames
                .FirstOrDefault(relativeFile => String.Equals(Path.GetFileName(relativeFile), fileName, StringComparison.OrdinalIgnoreCase));
            if (String.IsNullOrWhiteSpace(storageFile))
            {
                return null;
            }
            Uri storageFileUri = new Uri(new Uri(manifest.RootUri), storageFile);
            try
            {
                var blob = new CloudBlockBlob(storageFileUri);
                await blob.FetchAttributesAsync();
                return blob;
            }
            catch (Exception ex)
            {
                Trace.TraceWarning("Error obtaining reference to package blob [{0}]. Details: {1}", storageFileUri, ex);
                return null;
            }
        }
    }
}